// 接口

// 初探接口
function interface_eg01() {
  function printLabel(labelledObj: { label: string }) {
    console.log(labelledObj.label);
  }

  let myObj = { size: 10, label: 'Size 10 Object' };

  printLabel(myObj);
}

// 使用接口描述重写上边例子
function interface_eg01_rewrite() {
  interface labelledValue {
    label: string;
  }

  function printLabel(labelledObj: labelledValue) {
    console.log(labelledObj.label);
  }

  let myObj = { size: 10, label: 'Size 10 Object' };

  printLabel(myObj);
}

// 可选属性-接口里的属性不全都是必需的。
// 可选属性名字定义的后面加一个?符号
function interface_eg02() {
  interface SquareConfig {
    color?: string;
    width?: number;
  }

  interface SquareResult {
    color: string, area: number
  }

  interface SquareConfig_new {
    color?: string;
    width?: number;
    [propName: string]: any;
  }

  function createSquare(config: SquareConfig): SquareResult {
    let newSquare = { color: 'white', area: 100 }
    if (config.color) {
      newSquare.color = config.color;
    }
    if (config.width) {
      newSquare.area = config.width ** 2;
    }
    return newSquare
  }

  // 额外的属性检查
  let my_square = createSquare({ colour: "red", width: 100 } as SquareConfig)
  // 方法一 最简单的方法是使用类型断言
  // let my_square = createSquare({ colour: "red", width: 100 } as SquareConfig)
  // 方法二 最佳的方法是添加字符串索引签名（应用场景除了上面已经定义的属性，还会带有任意数量的其他属性）
  let my_square_new = createSquare({ colour: "red", width: 100 } as SquareConfig_new)

  console.log(my_square, my_square_new)
}

// 只读属性：一些对象属性只能在对象刚刚创建的时候修改其值。
function interface_eg03() {
  interface Point {
    readonly x: number;
    y: number;
  }

  let p: Point = {
    x: 10,
    y: 20
  }

  // p.x = 20

  // 只读数组 ReadonlyArray<T> T 代表类型，把所有可变的数据去掉了，确保数组创建完不能在被修改。
  let a: number[] = [1, 2, 3, 4];
  let ro: ReadonlyArray<number> = a;
  // ro[0] = 1
  // ro.push(4)
  // ro.length = 6
  // a = ro
}


// 函数类型
function interface_eg04() {
  interface searchFunc {
    (source: string, subString: string): boolean;
  }

  let mySearch: searchFunc;
  mySearch = function (source: string, subString: string): boolean {
    let result = source.search(subString);
    return result > 1;
  }

  // 重写上边例子
  let mySearch_new: searchFunc = function (src, sub) {
    let result = src.search(sub);
    return result > 1;
  }
}

// 可以引用类型：可索引类型具有一个 索引签名，它描述了对象索引的类型，还有相应的索引返回值类型。
// TypeScript支持两种索引签名：字符串和数字。 可以同时使用两种类型的索引，但是数字索引的返回值必须是字符串索引返回值类型的子类型。 数字会先转为字符串再去索引，两者要保持一致。
function interface_eg05() {
  interface StringArray {
    [index: number]: string;
  }

  let myArray: StringArray;
  myArray = ["Bob", "Fred"];

  let myStr: string = myArray[0];
}

// 类类型
// 实现接口--(没看明白)
function interface_eg06() {
  interface ClockInterface {
    currentTime: Date;
  }

  class Clock implements ClockInterface {
    currentTime: Date;
    constructor(h: number, m: number) {
      this.currentTime = h as unknown as Date
    }
  }
}

// 继承接口：和类一样，接口也可以相互继承
function interface_eg07() {
  interface Shape {
    color: string;
  }

  interface Square extends Shape {
    sideLength: number;
  }

  let square = <Square>{}

  square.color = 'White';
  square.sideLength = 10;
}

// 混合类型
function interface_eg08() {
  interface Counter {
    (start: number): number;
    interval: number;
    reset(): void;
  }

  function getCounter(): Counter {
    let counter = <Counter>function (start: number) {

    }
    counter.interval = 123;
    counter.reset = function () { 

    }
    return counter
  }

  let c = getCounter();
  c(10);
  c.reset();
  c.interval = 1000;
}

// 接口继承类


// 执行
// interface_eg02()